package ru.stepanovdn.trading.server.service.tinkoff

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.stereotype.Service
import ru.stepanovdn.trading.server.service.dictionary.InstrumentService
import ru.tinkoff.piapi.contract.v1.Quotation
import java.math.BigDecimal

import java.util.UUID
import ru.tinkoff.piapi.contract.v1.OrderDirection
import ru.tinkoff.piapi.contract.v1.OrderType
import ru.tinkoff.piapi.contract.v1.PostOrderResponse
import ru.tinkoff.piapi.contract.v1.MoneyValue

import java.math.RoundingMode

@Service
@ConditionalOnProperty(name = ["tinkoff.emulator"], havingValue = "false")
class TinkoffGRPCAPI : ATinkoffAPI() {
    override fun buy(instrument: InstrumentService.Instrument, price: BigDecimal, count: Int)
    : ITinkoffOrderAPI.OrderResult {
        val quantity: Int = count / instrument.lot
        val quotation: Quotation = Quotation.newBuilder()
            .setUnits(price.toLong())
            .setNano(price.remainder(BigDecimal.ONE).movePointRight(9).toInt())
            .build()
        val uuid: String = UUID.randomUUID().toString()
        log.info(
            "Send postOrderSync with: figi {}, quantity {}, quotation {}, direction {}, acc {}, type {}, id {}",
            instrument.figi,
            quantity,
            quotation,
            OrderDirection.ORDER_DIRECTION_BUY,
            accountId,
            OrderType.ORDER_TYPE_MARKET,
            uuid
        )

        if (isSandboxMode) {
            val result: PostOrderResponse = api!!.sandboxService.postOrderSync(
                instrument.figi!!, quantity.toLong(), quotation,
                OrderDirection.ORDER_DIRECTION_BUY, accountId!!, OrderType.ORDER_TYPE_MARKET, uuid
            )
            return ITinkoffOrderAPI.OrderResult(
                toBigDecimal(result.initialCommission, 8),
                toBigDecimal(result.executedOrderPrice, 4, price)
            )
        } else {
            val result: PostOrderResponse = api!!.ordersService.postOrderSync(
                instrument.figi!!, quantity.toLong(), quotation,
                OrderDirection.ORDER_DIRECTION_BUY, accountId!!, OrderType.ORDER_TYPE_MARKET, uuid
            )
            return ITinkoffOrderAPI.OrderResult(
                toBigDecimal(result.initialCommission, 8),
                toBigDecimal(result.executedOrderPrice, 4, price)
            )

        }
    }

    override fun sell(instrument: InstrumentService.Instrument, price: BigDecimal, count: Int)
    : ITinkoffOrderAPI.OrderResult {
        val quantity: Int = count / instrument.lot
        val quotation: Quotation = Quotation.newBuilder()
            .setUnits(price.toLong())
            .setUnits(price.remainder(BigDecimal.ONE).movePointRight(9).toInt().toLong())
            .build()
        val uuid: String = UUID.randomUUID().toString()
        log.info(
            "Send postOrderSync with: figi {}, quantity {}, quotation {}, direction {}, acc {}, type {}, id {}",
            instrument.figi,
            quantity,
            quotation,
            OrderDirection.ORDER_DIRECTION_SELL,
            accountId,
            OrderType.ORDER_TYPE_MARKET,
            uuid
        )

        if (isSandboxMode) {
            val result: PostOrderResponse = api!!.sandboxService.postOrderSync(
                instrument.figi!!, quantity.toLong(), quotation,
                OrderDirection.ORDER_DIRECTION_SELL, accountId!!, OrderType.ORDER_TYPE_MARKET, uuid
            )
            return ITinkoffOrderAPI.OrderResult(
                toBigDecimal(result.initialCommission, 8),
                toBigDecimal(result.executedOrderPrice, 4, price)
            )
        } else {
            val result: PostOrderResponse = api!!.ordersService.postOrderSync(
                instrument.figi!!, quantity.toLong(), quotation,
                OrderDirection.ORDER_DIRECTION_SELL, accountId!!, OrderType.ORDER_TYPE_MARKET, uuid
            )
            return ITinkoffOrderAPI.OrderResult(
                toBigDecimal(result.initialCommission, 8),
                toBigDecimal(result.executedOrderPrice, 4, price)
            )

        }
    }

    companion object {
        fun toBigDecimal(value: MoneyValue?, scale: Int?): BigDecimal {
            if (value == null) {
                return BigDecimal.ZERO
            }
            val result: BigDecimal =
                BigDecimal.valueOf(value.units).add(BigDecimal.valueOf(value.nano.toLong(), 9))
            if (scale != null) {
                return result.setScale(scale, RoundingMode.HALF_EVEN)
            }
            return result
        }

        fun toBigDecimal(value: MoneyValue?, scale: Int?, defaultIfZero: BigDecimal?): BigDecimal? {
            val amount: BigDecimal = toBigDecimal(value, scale)
            if (amount.compareTo(BigDecimal.ZERO) == 0) {
                return defaultIfZero
            }
            return amount
        }
    }

}
